package com.weifei.core.aop;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class WFMethodInvocation implements WFJointPoint{
    /**
     * 代理对象
     */
    private Object proxy;
    /**
     * 代理目标方法
     */
    private Method method;
    /**
     * 代理目标对象
     */
    private Object target;
    /**
     * 代理目标类
     */
    private Class targetClass;
    /**
     * 代理方法的实参列表
     */
    private Object[] arguments;
    /**
     * 回调方法链
     */
    private List<Object> interceptorsAndDynamicMethodMatchers;
    /**
     * 保存自定义属性
     */
    private Map<String, Object> userAttributes;

    private int currentInterceptorIndex = -1;

    public WFMethodInvocation(Object proxy, Object target, Method method,
                              Object[] arguments, Class targetClass,
                              List<Object> interceptorsAndDynamicMethodMatchers) {
        this.proxy = proxy;
        this.target = target;
        this.method = method;
        this.arguments = arguments;
        this.targetClass = targetClass;
        this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;

    }

    public Object proceed() throws InvocationTargetException, IllegalAccessException {
        //先执行拦截器链，再执行joinPoint
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return this.method.invoke(this.target, this.arguments);
        }
        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++ this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof WFMethodInterceptor) {
            WFMethodInterceptor mi = (WFMethodInterceptor) interceptorOrInterceptionAdvice;
             return mi.invoke(this);
        } else {
            // 递归调用
            return proceed();
        }
    }

    @Override
    public Method getMethod() {
        return this.method;
    }

    @Override
    public Object[] getArguments() {
        return this.arguments;
    }

    @Override
    public Object getThis() {
        return this.target;
    }

    @Override
    public void setUserAttribute(String key, Object value) {
        if (value != null) {
            if (this.userAttributes == null) {
                this.userAttributes = new HashMap<>();
            }
            this.userAttributes.put(key , value);
        } else {
            if (this.userAttributes != null) {
                this.userAttributes.remove(key);
            }
        }
    }

    @Override
    public Object getUserAttribute(String key) {
        return this.userAttributes != null ? this.userAttributes.get(key) : null;
    }
}
